﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using ZT.Core.Enums;
using ZT.Core.Service;

namespace ZT.Core.MiddleWare
{
    public class ZTActionFilter : ActionFilterAttribute
    {
        private ORM.IUnitOfWork work = null;
        private ORM.IRepository<Model.Auth_User> rpsUser = null;
        private ORM.IRepository<Model.Auth_UserProfile> rpsProfile = null;
        private ORM.IRepository<Model.Auth_KeyDetail> rpsKeyDetail = null;
        private ORM.IRepository<Model.Auth_UserRole> rpsRole = null;
        private ORM.IRepository<Model.Auth_RoleAuthScope> rpsRoleScope = null;
        public ZTActionFilter(ORM.IUnitOfWork unitOfWork)
        {
            work = unitOfWork;
            rpsUser = work.Repository<Model.Auth_User>();
            rpsProfile = work.Repository<Model.Auth_UserProfile>();
            rpsKeyDetail = work.Repository<Model.Auth_KeyDetail>();
            rpsRole = work.Repository<Model.Auth_UserRole>();
            rpsRoleScope = work.Repository<Model.Auth_RoleAuthScope>();
        }
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var ctrDesciptor = context.ActionDescriptor as ControllerActionDescriptor;

            if (ctrDesciptor != null)
            {
                var cisDefined = ctrDesciptor.ControllerTypeInfo.GetCustomAttributes(inherit: true)
                    .Any(q => q.GetType().Equals(typeof(AllowAnymousAttribute)));
                var misDefined = ctrDesciptor.MethodInfo.GetCustomAttributes(inherit: true)
                    .Any(q => q.GetType().Equals(typeof(AllowAnymousAttribute)));

                var croute = (RouteAttribute)ctrDesciptor.ControllerTypeInfo.GetCustomAttributes(typeof(RouteAttribute), inherit: true).FirstOrDefault();

                var mroute = (RouteAttribute)ctrDesciptor.MethodInfo.GetCustomAttributes(typeof(RouteAttribute), inherit: true).FirstOrDefault();

                var route = croute.Template + "/" + mroute.Template;  //当前路由


                if (!cisDefined && !misDefined)
                {
                    var token = context.HttpContext.Request.Headers.GetCommaSeparatedValues("token").FirstOrDefault();

                    if (string.IsNullOrEmpty(token))
                        throw new Exception("非法请求");

                    //鉴权及权限验证
                    var user = rpsUser.Queryable(q => q.Token == token).FirstOrDefault();
                    if (user == null)
                    {
                        throw new Exception("非法请求");
                    }
                    //token的有效期更新，和过期检查
                    if ((DateTime.Now - user.TokenValidTime).TotalMinutes > 0)
                    {
                        throw new Exception("登录超时");
                    }
                    if (user.State != UserState.Normal)
                        throw new Exception("用户已被停用，请联系管理员!");
                    user.TokenValidTime = DateTime.Now.AddMinutes(30);
                    rpsUser.Update(user);
                    work.Commit();
                    //为controller的所有service赋值 当前user
                    var ctr = (BaseController)context.Controller;
                    ctr.AppUser = new Dto.AppUser
                    {
                        UserProfile = rpsProfile.GetModel(p => p.UserId == user.Id),
                        UserInfo = user
                    };
                    foreach (var s in ctr.Services)
                    {
                        var sv = (ServiceBase)s;
                        sv.AppUser = ctr.AppUser;
                    }
                    if (AuthKey.AuthKeys == null)
                    {
                        AuthKey.AuthKeys = rpsKeyDetail.Queryable().ToList();//此处说明:AsEnumerable不会将数据存到本地，ToList会存在本地。如果用前者则必须每次都查询
                    }                    
                    if (AuthKey.AuthKeys.Any(q => q.ActionFullName == route))//如果需要权限控制则验证用户权限
                    {
                        var userRoles = rpsRole.Queryable(p=>p.Login==user.Login).Select(s=>s.RoleId);
                        var authKeys = rpsRoleScope.Queryable(p=>userRoles.Contains(p.RoleId)).Select(s=>s.AuthKey);
                        var auth = AuthKey.AuthKeys.Where(p=>authKeys.Contains(p.AuthKey));
                        if (!auth.Any(q => q.ActionFullName == route))
                        {
                            throw new Exception("无权操作");
                        }
                    }
                }
                //else if (croute.Template == "wxapi" && mroute.Template != "getmsg" && mroute.Template != "authopenid" && mroute.Template != "authcode" && mroute.Template != "downloadFile/{ID:Guid}" && mroute.Template != "getAcceseToken")
                //{
                //    StringValues userAgent = "";
                //    context.HttpContext.Request.Headers.TryGetValue("User-Agent", out userAgent);
                //    if (!userAgent.ToString().Contains("MicroMessenger"))
                //        throw new Exception("请使用微信客户端访问!");
                //    var openID = context.HttpContext.Request.Headers.GetCommaSeparatedValues("openid").FirstOrDefault();
                //    if (string.IsNullOrEmpty(openID))
                //        throw new Exception("非法请求");

                //    var ctr = (BaseController)context.Controller;
                //    foreach (var s in ctr.Services)
                //    {
                //        var sv = (ServiceBase)s;
                //        sv.OpenID = openID;
                //    }
                //}

                base.OnActionExecuting(context);
            }
            else
            {

                base.OnActionExecuting(context);
            }
        }
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            var ctl = (BaseController)context.Controller;
            var res = context.Result as ObjectResult;
            ctl.ResponseData = JsonConvert.SerializeObject(res.Value);
            ctl.WriteLog();
            base.OnActionExecuted(context);
        }
    }

    /// <summary>
    /// 匿名Action 什么都不做
    /// </summary>
    public class AllowAnymousAttribute : Attribute
    {

    }
    /// <summary>
    /// 请求头参数
    /// </summary>
    public class AddHeaderParatmeter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            if (operation.Parameters == null)
                operation.Parameters = new List<OpenApiParameter>();
            var attrs = context.ApiDescription.ActionDescriptor.AttributeRouteInfo;

            //先判断是否是匿名访问,
            var descriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
            if (descriptor != null)
            {
                var actionAttributes = descriptor.MethodInfo.GetCustomAttributes(inherit: true);
                var croute = (RouteAttribute)descriptor.ControllerTypeInfo.GetCustomAttributes(typeof(RouteAttribute), inherit: true).FirstOrDefault();
                var mroute = (RouteAttribute)descriptor.MethodInfo.GetCustomAttributes(typeof(RouteAttribute), inherit: true).FirstOrDefault();
                bool isAnonymous = actionAttributes.Any(a => a is AllowAnymousAttribute);
                //非匿名的方法,链接中添加token值
                if (!isAnonymous)
                {
                    operation.Parameters.Add(new OpenApiParameter
                    {
                        Name = "Token",
                        Schema=new OpenApiSchema
                        {  
                            Type="string"
                        },
                        In = ParameterLocation.Header,//query header body path formData
                        Required = true //是否必选
                    });
                }
            }
        }
    }
    /// <summary>
    /// Swagger 控制器注释
    /// </summary>
    public class SwaggerDocTag : IDocumentFilter
    {
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            swaggerDoc.Tags= GetControllerDesc();
        }

        /// <summary>
        /// 从xml注释中读取控制器注释
        /// </summary>
        /// <returns></returns>
        private List<OpenApiTag> GetControllerDesc()
        {
            List<OpenApiTag> tagList = new List<OpenApiTag>();

            var xmlpath = string.Format(string.Format("{0}/ZT.Core.API.xml", System.AppDomain.CurrentDomain.BaseDirectory));
            if (!File.Exists(xmlpath))//检查xml注释文件是否存在
                return tagList;

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(xmlpath);

            string memberName = string.Empty;//xml三级节点的name属性值
            string controllerName = string.Empty;//控制器完整名称
            string key = string.Empty;//控制器去Controller名称
            string value = string.Empty;//控制器注释

            foreach (XmlNode node in xmlDoc.SelectNodes("//member"))//循环三级节点member
            {
                memberName = node.Attributes["name"].Value;
                if (memberName.StartsWith("T:"))//T:开头的代表类
                {
                    string[] arrPath = memberName.Split('.');
                    controllerName = arrPath[arrPath.Length - 1];
                    if (controllerName.EndsWith("Controller"))//Controller结尾的代表控制器
                    {
                        XmlNode summaryNode = node.SelectSingleNode("summary");//注释节点
                        key = controllerName.Remove(controllerName.Length - "Controller".Length, "Controller".Length);
                        if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !tagList.Contains(new OpenApiTag { Name = key }))
                        {
                            value = summaryNode.InnerText.Trim();
                            tagList.Add(new OpenApiTag { Name =key, Description = value });
                        }
                    }
                }
            }
            return tagList;
        }
    }

    /// <summary>
    /// 异常处理过滤器
    /// </summary>
    public class ExceptionFilter : ExceptionFilterAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            if (!context.ExceptionHandled)
            {
                var retmp = new Utility.ResultModel<bool>(context.Exception);
                var re = JsonConvert.SerializeObject(retmp);
                context.Result = new ContentResult
                {
                    ContentType = "application/json;charset=utf-8",
                    StatusCode = StatusCodes.Status200OK,
                    Content = re
                };
            }
            context.ExceptionHandled = true; //异常已处理了
        }
    }
    /// <summary>
    /// 权限Key
    /// </summary>
    public static class AuthKey
    {
        public static IEnumerable<Model.Auth_KeyDetail> AuthKeys { get; set; }
    }
}
